package com.tuo51.evaluate.service.appuser;

import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.google.common.collect.Maps;
import com.tuo51.evaluate.domain.appusers.PayOrder;
import com.tuo51.evaluate.domain.appusers.PayWxOrder;
import com.tuo51.evaluate.domain.appusers.PromoCode;
import com.tuo51.evaluate.domain.appusers.Vip;
import com.tuo51.evaluate.domain.business.Order;
import com.tuo51.evaluate.domain.business.Product;
import com.tuo51.core.domain.staffs.Staff;
import com.tuo51.evaluate.repositories.appusers.PayOrderRepository;
import com.tuo51.evaluate.repositories.appusers.PayWxOrderRepository;
import com.tuo51.evaluate.repositories.appusers.PromoCodeRepository;
import com.tuo51.core.service.StaffService;
import com.tuo51.foudation.constant.APPConstants;
import com.tuo51.foudation.exception.BusinessException;
import com.tuo51.foudation.model.APPErrorCode;
import com.tuo51.foudation.model.ErrorCode;
import com.tuo51.foudation.utils.PaymentUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.tuo51.foudation.constant.APPConstants.*;
import static com.tuo51.foudation.constant.APPConstants.PayObjectType.REPORT;
import static com.tuo51.foudation.constant.APPConstants.PayObjectType.VIP;

@Service
public class PaymentService {

    @Value("${wxpay.notify_url}")
    private String wxNotifyURL;

    @Autowired
    StaffService staffService;

    @Autowired
    WxPayService wxPayService;

    @Autowired
    PayWxOrderRepository payWxOrderRepository;

    @Autowired
    ProductService productService;

    @Autowired
    PromoCodeRepository promoCodeRepository;

    @Autowired
    PayOrderRepository payOrderRepository;

    public List<PayOrder> getAllOrdersForUser(String userId){
        return payOrderRepository.findAllByUserIdOrderByCreateTimeDesc(userId);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public Map<String,Object> createWxVIPOrder(String userId, long productId, String code ) throws WxPayException, JSONException {
        return createWxOrder(userId, productId, VIP.getValue(), userId, code, wxPayService);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public Map<String,Object> createWxOrder(String userId, long productId, int payObjectType, String payObjectId, String code, WxPayService service ) throws WxPayException, JSONException {
        Staff user = staffService.getById(userId);


        Product product = productService.getProductById(productId);

        int price = product.getPrice(); //总价

        PromoCode promoCode = null;
        if (StringUtils.isNotEmpty(code)) {
            promoCode = promoCodeRepository.getByCode(code);
        }
        if(promoCode != null){
            if (promoCode.getSalesQuota() <= promoCode.getSalesQuantity()){
                throw new BusinessException(ErrorCode.PROMOCODE_OUTCOUNT, "优惠码用完了");
            }

            if(promoCode.getProductId() == null || promoCode.getProductId() == productId){
                Integer newPrice = promoCode.calc(price);
                price = newPrice;
            }
        }

        String name = product.getName();

        //创建本地订单
        PayWxOrder payWxOrder = new PayWxOrder();
        payWxOrderRepository.save(payWxOrder);

        String outTradeNo = payWxOrder.getId();

        //请求远程订单
        JSONObject attach = new JSONObject()
                .put(PRODUCT_ID_KEY, productId)
                .put(PAY_OBJECT_TYPE_KEY, payObjectType)
                .put(PAY_OBJECT_ID_KEY, payObjectId)
                .put(USER_ID, userId);

        WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = null;
        try {
            wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                    .openid(StringUtils.isNotBlank(user.getOpenidMA())?user.getOpenidMA():user.getOpenidMP())
                    .outTradeNo(outTradeNo)
                    .totalFee(price)
                    .body(URLDecoder.decode(name, "utf-8"))
                    .tradeType(APPConstants.TradeTypeWX.JSAPI.getName())
                    .spbillCreateIp(PaymentUtils.getLocalIP())
                    .attach(attach.toString())
                    .notifyUrl(wxNotifyURL)
                    .build();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        WxPayMpOrderResult wxPayAppOrderResult = service.createOrder(wxPayUnifiedOrderRequest);

        payWxOrder.setUserId(userId);
        payWxOrder.setAppId(wxPayUnifiedOrderRequest.getAppid());
        payWxOrder.setAttach(wxPayUnifiedOrderRequest.getAttach());
        payWxOrder.setOrderNo(wxPayUnifiedOrderRequest.getOutTradeNo());
        payWxOrder.setTradeType(wxPayUnifiedOrderRequest.getTradeType());
        payWxOrder.setTotalFee(wxPayUnifiedOrderRequest.getTotalFee());  //总价
        payWxOrder.setProductIssueStatus(APPConstants.ProductIssueStatus.ISSUE_NON.getValue());
        payWxOrder.setCreateTime(DateTime.now().toDate());
        payWxOrder.setStatus(APPConstants.PayOrderStatus.NON_PAYMENT.getValue());

        if(promoCode != null) {
            payWxOrder.setPromoCodeId(promoCode.getId());
        }

        payWxOrderRepository.save(payWxOrder);

        Map<String,Object> result = Maps.newHashMap();

        result.put("wxPayResult",wxPayAppOrderResult);
        result.put("outTradeNo",outTradeNo);

        return result;
    }


    public <T extends Order> T getOrderByOrderNo(String orderNo, APPConstants.PayType payType){
        if(payType.getValue() == APPConstants.PayType.WXPAY.getValue()){
            return (T)payWxOrderRepository.findOneByOrderNo(orderNo);
        }

        return null;
    }

    public <T extends Order> T updateOrder(Order order, APPConstants.PayType payType){
        if(payType.getValue() == APPConstants.PayType.WXPAY.getValue()){
            return (T)payWxOrderRepository.save((PayWxOrder) order);
        }

        return null;
    }

    @Transactional
    public boolean finishWxOrder(PayWxOrder payWxOrder, APPConstants.PayType payType)  {
        String outTradeNo = null;

        try {

            if (Objects.nonNull(payWxOrder)
                    && StringUtils.isNotBlank(payWxOrder.getResultCode())
                    && payWxOrder.getResultCode().equals("SUCCESS")) {

                if(payWxOrder.getProductIssueStatus().intValue() == APPConstants.ProductIssueStatus.ISSUE_SUCCESS.getValue()){
                    return true;
                }

                outTradeNo = payWxOrder.getOrderNo();

                JSONObject attachJson = new JSONObject(payWxOrder.getAttach());

//                .put(PRODUCT_ID_KEY, productId)
//                        .put(PAY_OBJECT_TYPE_KEY, payObjectType)
//                        .put(PAY_OBJECT_ID_KEY, payObjectId)
//                        .put(USER_ID, userId);

                final long productId = attachJson.getLong(PRODUCT_ID_KEY);
                final long payObjectType = attachJson.getLong(PAY_OBJECT_TYPE_KEY);
                String payObjectId = attachJson.getString(PAY_OBJECT_ID_KEY);
                String targetUserId = attachJson.getString(USER_ID);

                String userId = payWxOrder.getUserId();

                Vip product = productService.getProductById(productId);

                //订单生效
                if (payObjectType == VIP.getValue()){
                    productService.productVIPIssue(userId, product);
                }else if(payObjectType == REPORT.getValue() ){
                    productService.productReportIssue(payObjectId);
                }
                payWxOrder.setProductIssueStatus(APPConstants.ProductIssueStatus.ISSUE_SUCCESS.getValue());
                this.updateOrder(payWxOrder, APPConstants.PayType.WXPAY);

                //生成订单记录
                PayOrder payOrder = new PayOrder();
                payOrder.setAmount(payWxOrder.getTotalFee());
                payOrder.setOutTradeNo(outTradeNo);
                payOrder.setOutTradeType(APPConstants.PayType.WXPAY.getValue());
                payOrder.setProductId(productId);

                payOrder.setProductName(product.getName());
                payOrder.setUserId(userId);
                payOrder.setCreateTime(DateTime.now().toDate());

                //更新折扣码
                if(StringUtils.isNotBlank(payWxOrder.getPromoCodeId())){
                    PromoCode promoCode = promoCodeRepository.getById(payWxOrder.getPromoCodeId());
                    promoCode.setSalesQuantity(promoCode.getSalesQuantity() + 1);
                    promoCode.setSales(promoCode.getSales() + payWxOrder.getTotalFee());
                    promoCodeRepository.save(promoCode);
                }

                payOrderRepository.save(payOrder);

                return true;
            } else {
                String message = String.format("完结订单失败:outTradeNo={},payType={}", outTradeNo, payType.getName());
                throw new BusinessException(APPErrorCode.ORDER_STATUS_ERROR.getCode(), message);
            }


        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            String message = String.format("完结订单失败:outTradeNo={},payType={}", outTradeNo, payType.getName());
            throw new BusinessException(APPErrorCode.UPDATE_PAY_ORDER_FAIL.getCode(), message, e);
        }

    }
}
